﻿using System;
using System.Text;
using System.Runtime.InteropServices;

using PickGold.Api;

namespace PickGold.Com
{
	/// <summary>
	/// 快捷方式类
	/// </summary>
	public class Shortcut
	{
		#region inner class

		[Flags]
		internal enum SLR_MODE : uint
		{
			SLR_INVOKE_MSI = 0x80,
			SLR_NOLINKINFO = 0x40,
			SLR_NO_UI = 0x1,
			SLR_NOUPDATE = 0x8,
			SLR_NOSEARCH = 0x10,
			SLR_NOTRACK = 0x20,
			SLR_UPDATE = 0x4,
			SLR_NO_UI_WITH_MSG_PUMP = 0x101
		}

		[Flags]
		internal enum STGM_ACCESS : uint
		{
			STGM_READ = 0x00000000,
			STGM_WRITE = 0x00000001,
			STGM_READWRITE = 0x00000002,
			STGM_SHARE_DENY_NONE = 0x00000040,
			STGM_SHARE_DENY_READ = 0x00000030,
			STGM_SHARE_DENY_WRITE = 0x00000020,
			STGM_SHARE_EXCLUSIVE = 0x00000010,
			STGM_PRIORITY = 0x00040000,
			STGM_CREATE = 0x00001000,
			STGM_CONVERT = 0x00020000,
			STGM_FAILIFTHERE = 0x00000000,
			STGM_DIRECT = 0x00000000,
			STGM_TRANSACTED = 0x00010000,
			STGM_NOSCRATCH = 0x00100000,
			STGM_NOSNAPSHOT = 0x00200000,
			STGM_SIMPLE = 0x08000000,
			STGM_DIRECT_SWMR = 0x00400000,
			STGM_DELETEONRELEASE = 0x04000000
		}

		internal const uint SLGP_SHORTPATH = 0x01;
		internal const uint SLGP_UNCPRIORITY = 0x02;
		internal const uint SLGP_RAWPATH = 0x04;

		[ComImport]
		[Guid("000214F9-0000-0000-C000-000000000046")]
		[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
		internal interface IShellLinkW
		{
			[PreserveSig()]
			int GetPath([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, ref WIN32_FIND_DATA pfd, uint fFlags);

			[PreserveSig()]
			int GetIDList(out IntPtr ppidl);

			[PreserveSig()]
			int SetIDList(IntPtr pidl);

			[PreserveSig()]
			int GetDescription([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxName);

			[PreserveSig()]
			int SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName);

			[PreserveSig()]
			int GetWorkingDirectory([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath);

			[PreserveSig()]
			int SetWorkingDirectory(
			   [MarshalAs(UnmanagedType.LPWStr)] string pszDir);

			[PreserveSig()]
			int GetArguments([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath);

			[PreserveSig()]
			int SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs);

			[PreserveSig()]
			int GetHotkey(out ushort pwHotkey);

			[PreserveSig()]
			int SetHotkey(ushort pwHotkey);

			[PreserveSig()]
			int GetShowCmd(out uint piShowCmd);

			[PreserveSig()]
			int SetShowCmd(uint piShowCmd);

			[PreserveSig()]
			int GetIconLocation([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, int cchIconPath, out int piIcon);

			[PreserveSig()]
			int SetIconLocation(
			   [MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon);

			[PreserveSig()]
			int SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, uint dwReserved);

			[PreserveSig()]
			int Resolve(IntPtr hWnd, uint fFlags);

			[PreserveSig()]
			int SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile);
		}

		[ComImport]
		[Guid("0000010B-0000-0000-C000-000000000046")]
		[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
		internal interface IPersistFile
		{
			[PreserveSig()]
			int GetClassID(out Guid pClassID);

			[PreserveSig()]
			int IsDirty();

			[PreserveSig()]
			int Load([MarshalAs(UnmanagedType.LPWStr)] string pszFileName, uint dwMode);

			[PreserveSig()]
			int Save([MarshalAs(UnmanagedType.LPWStr)] string pszFileName, [MarshalAs(UnmanagedType.Bool)] bool fRemember);

			[PreserveSig()]
			int SaveCompleted([MarshalAs(UnmanagedType.LPWStr)] string pszFileName);

			[PreserveSig()]
			int GetCurFile([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath);
		}

		[ComImport]
		[Guid("00021401-0000-0000-C000-000000000046")]
		[ClassInterface(ClassInterfaceType.None)]
		private class CShellLink { }

		#endregion

		internal static IShellLinkW CreateShellLink()
		{
			return (IShellLinkW)new CShellLink();
		}

		private const int MAX_DESCRIPTION_LENGTH = 512;
		private const int MAX_PATH = 512;

		private IShellLinkW _link;

		/// <summary>
		/// 构造一个快捷方式对象
		/// </summary>
		public Shortcut()
		{
			this._link = CreateShellLink();
		}

		/// <summary>
		/// 使用指定路径构造一个快捷方式对象
		/// </summary>
		/// <param name="path">快捷方式路径</param>
		public Shortcut(string path)
			: this()
		{
			Marshal.ThrowExceptionForHR(this._link.SetPath(path));
		}

		/// <summary>
		/// 路径
		/// </summary>
		public string Path
		{
			get
			{
				var fdata = new WIN32_FIND_DATA();
				var path = new StringBuilder(MAX_PATH, MAX_PATH);
				Marshal.ThrowExceptionForHR(this._link.GetPath(path, path.MaxCapacity, ref fdata, SLGP_UNCPRIORITY));
				return path.ToString();
			}
			set
			{
				Marshal.ThrowExceptionForHR(this._link.SetPath(value));
			}
		}

		/// <summary>
		/// 描述
		/// </summary>
		public string Description
		{
			get
			{
				StringBuilder desc = new StringBuilder(MAX_DESCRIPTION_LENGTH, MAX_DESCRIPTION_LENGTH);
				Marshal.ThrowExceptionForHR(this._link.GetDescription(desc, desc.MaxCapacity));
				return desc.ToString();
			}
			set
			{
				Marshal.ThrowExceptionForHR(this._link.SetDescription(value));
			}
		}

		/// <summary>
		/// 相对路径
		/// </summary>
		public string RelativePath
		{
			set
			{
				Marshal.ThrowExceptionForHR(this._link.SetRelativePath(value, 0));
			}
		}

		/// <summary>
		/// 工作目录
		/// </summary>
		public string WorkingDirectory
		{
			get
			{
				StringBuilder dir = new StringBuilder(MAX_PATH, MAX_PATH);
				Marshal.ThrowExceptionForHR(this._link.GetWorkingDirectory(dir, dir.MaxCapacity));
				return dir.ToString();
			}
			set
			{
				Marshal.ThrowExceptionForHR(this._link.SetWorkingDirectory(value));
			}
		}

		/// <summary>
		/// 参数
		/// </summary>
		public string Arguments
		{
			get
			{
				StringBuilder args = new StringBuilder(MAX_PATH, MAX_PATH);
				Marshal.ThrowExceptionForHR(this._link.GetArguments(args, args.MaxCapacity));
				return args.ToString();
			}
			set
			{
				Marshal.ThrowExceptionForHR(this._link.SetArguments(value));
			}
		}

		/// <summary>
		/// 快捷键
		/// </summary>
		public ushort HotKey
		{
			get
			{
				ushort key = 0;
				Marshal.ThrowExceptionForHR(this._link.GetHotkey(out key));
				return key;
			}
			set
			{
				Marshal.ThrowExceptionForHR(this._link.SetHotkey(value));
			}
		}

		/// <summary>
		/// 按指定窗口句柄解析
		/// </summary>
		/// <param name="hwnd">窗口句柄</param>
		/// <param name="flags">标志</param>
		public void Resolve(IntPtr hwnd, uint flags)
		{
			Marshal.ThrowExceptionForHR(this._link.Resolve(hwnd, flags));
		}

		/// <summary>
		/// 按指定窗口句柄解析
		/// </summary>
		/// <param name="hwnd">窗口句柄</param>
		public void Resolve(IntPtr hwnd)
		{
			this.Resolve(hwnd, 0);
		}

		/// <summary>
		/// 解析当前进程
		/// </summary>
		public void Resolve()
		{
			this.Resolve(IntPtr.Zero, (uint)SLR_MODE.SLR_NO_UI);
		}

		private IPersistFile AsPersist
		{
			get { return ((IPersistFile)this._link); }
		}

		/// <summary>
		/// 保存快捷方式
		/// </summary>
		/// <param name="fileName">文件名</param>
		public void Save(string fileName)
		{
			int hres = this.AsPersist.Save(fileName, true);
			Marshal.ThrowExceptionForHR(hres);
		}

		/// <summary>
		/// 加载快捷方式
		/// </summary>
		/// <param name="fileName">文件名</param>
		public void Load(string fileName)
		{
			int hres = this.AsPersist.Load(fileName, (uint)STGM_ACCESS.STGM_READ);
			Marshal.ThrowExceptionForHR(hres);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="shortcut"></param>
		/// <param name="target"></param>
		/// <param name="arguments"></param>
		/// <param name="startup"></param>
		/// <param name="description"></param>
		public static void Create(string shortcut, string target, string arguments = null, string startup = null, string description = null)
		{
			var s = new Shortcut();
			s.Path = target;
			if (!string.IsNullOrWhiteSpace(arguments))
				s.Arguments = arguments;
			if (!string.IsNullOrWhiteSpace(startup))
				s.WorkingDirectory = startup;
			else if (startup == ".")
				s.WorkingDirectory = System.IO.Path.GetDirectoryName(target);
			if (!string.IsNullOrWhiteSpace(description))
				s.Description = description;
			s.Save(shortcut);
		}
	}
	/// <summary/>
	public sealed class ShortcutFile : Shortcut { }
}
